package com.zzh.lib.recorder;

import android.annotation.SuppressLint;
import android.media.MediaRecorder;
import android.os.Build;

import com.zzh.lib.recorder.def.IRecorder;
import com.zzh.lib.recorder.utils.HLog;
import com.zzh.lib.recorder.utils.LameUtils;

import java.io.File;

/**
 * Created by ZZH on 2022/11/28
 *
 * @Date: 2022/11/28 14:17
 * @Email: zzh_hz@126.com
 * @QQ: 1299234582
 * @Author: zzh
 * @Description: 使用MediaRecorder 进行录制。
 */
public class HMediaRecorder implements IRecorder {

    private MediaRecorder mRecorder;
    private HMediaRecorderParams mRecorderParams;
    private State mState = State.Idle;
    private File mDirFile;
    private boolean mIsInit;
    private long mStartTime;
    private File mRecordFile;
    private Thread mMaxAmplitudeThread;

    /**
     * 初始化录音器
     */
    public void init() {
        mDirFile = new File(mRecorderParams.getFileSaveDir());
        if (mIsInit) return;

        try {
            mRecorder = new MediaRecorder();
            mRecorder.setOnErrorListener(mInternalOnErrorListener);
            mState = State.Idle;
            mIsInit = true;
        } catch (Exception e) {
            notifyException(e);
        }
    }

    private void checkInit() {
        if (!mIsInit) throw new RuntimeException(this + " has not been init");
    }

    /**
     * 设置录音参数
     *
     * @param params
     */
    public void setRecorderParams(HMediaRecorderParams params) {
        if (params != null) {
            mRecorderParams = params;
        } else {
            mRecorderParams = HMediaRecorderParams.DEFAULT;
        }
    }

    private HMediaRecorderParams getRecorderParams() {
        if (mRecorderParams == null) mRecorderParams = HMediaRecorderParams.DEFAULT;
        return mRecorderParams;
    }

    /**
     * 返回当前状态
     *
     * @return
     */
    @Override
    public State getState() {
        return mState;
    }

    private boolean checkDirectory() {

        if (mDirFile == null) {
            notifyException(new RuntimeException("create dir file failed"));
            return false;
        }

        if (mDirFile.exists()) return true;

        try {
            return mDirFile.mkdirs();
        } catch (Exception e) {
            notifyException(e);
            return false;
        }
    }

    private final MediaRecorder.OnErrorListener mInternalOnErrorListener = new MediaRecorder.OnErrorListener() {
        @Override
        public void onError(MediaRecorder mr, int what, int extra) {
            stopRecorder(false);
            notifyException(new RuntimeException(mr + ":" + what + "," + extra));
        }
    };

    private void setState(State state) {
        final State oldState = mState;
        if (oldState == state) return;

        mState = state;

        if (mState == State.Released) mIsInit = false;

        if (mRecorderParams.getOnStateChangeCallback() != null) {
            mRecorderParams.getOnStateChangeCallback().onStateChanged(this, oldState, mState);
        }

    }

    /**
     * 开始录音
     */
    @Override
    public void start(File file) {
        checkInit();
        if (mState == State.Idle) {
            startRecorder(file);
        }
    }

    /**
     * 停止录音
     */
    public void stop() {
        if (mState == State.Recording) {
            stopRecorder(true);
        }
    }

    @Override
    public void start() {
        startRecorder(mRecordFile);
    }

    public void pause() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            setState(State.PAUSE);
            mRecorder.pause();
        }
    }

    public void resume() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            setState(State.Recording);
            mRecorder.resume();
        }
    }

    /**
     * 释放资源，一般在录音界面关闭的时候调用，调用后如果想继续使用的话需要先调用init(context)方法初始化
     */
    public void release() {
        releaseRecorder();
        try {
            if (mMaxAmplitudeThread != null) {
                mMaxAmplitudeThread.interrupt();
                mMaxAmplitudeThread = null;
            }
        } catch (Exception ex) {
            HLog.isMRLogE(ex.getMessage());
        }

    }

    @Override
    public void reset() {
        mRecorder.reset();
    }

    @Override
    public boolean isRecorder() {
        return mState == State.Recording;
    }

    @Override
    public File getRecordFile() {
        return mRecordFile;
    }

    @Override
    public HMediaRecorder setRecordFile(File file) {
        this.mRecordFile = file;
        return this;
    }

    @SuppressLint("WrongConstant")
    private void startRecorder(File file) {
        if (!checkDirectory()) return;
        final HMediaRecorderParams params = getRecorderParams();
        if (file == null) {
            String fileType = ".aac";
            if (params.getOutputFormat() == MediaRecorder.OutputFormat.AMR_WB || params.getOutputFormat() == MediaRecorder.OutputFormat.AMR_NB) {
                fileType = ".amr";
            }
            file = LameUtils.createDefaultFileUnderDir(mDirFile, fileType);
        }

        if (file == null) return;

        mRecordFile = file;

        HLog.isMRLog("录音文件路径：" + mRecordFile.getAbsolutePath());

        try {

            mRecorder.setAudioSource(params.getAudioSource());
            mRecorder.setOutputFormat(params.getOutputFormat());
            mRecorder.setAudioEncoder(params.getAudioEncoder());
            mRecorder.setAudioChannels(params.getAudioChannels());
            mRecorder.setAudioEncodingBitRate(params.getAudioEncodingBitRate());
            mRecorder.setAudioSamplingRate(params.getAudioSamplingRate());
            mRecorder.setOutputFile(mRecordFile.getAbsolutePath());
            mRecorder.prepare();
            mRecorder.start();
            mStartTime = System.currentTimeMillis();
            setState(State.Recording);
            if (params.getOnMaxAmplitudeListener() != null) {
                startTimeInBackgroundMaxAmplitude();
            }
        } catch (Exception e) {
            notifyException(e);
        }
    }

    private final int BASE = 1;

    private void startTimeInBackgroundMaxAmplitude() {
        if (mMaxAmplitudeThread == null) {
            mMaxAmplitudeThread = new Thread(() -> {
                while (getState() == State.Recording && getRecorderParams().getOnMaxAmplitudeListener() != null) {

                    double ratio = (double) mRecorder.getMaxAmplitude() / BASE;
                    double db = 0;// 分贝
                    if (ratio > 1) db = 20 * Math.log10(ratio);

                    getRecorderParams().getOnMaxAmplitudeListener().onMaxAmplitude(db);
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                }
            });
        }
        mMaxAmplitudeThread.start();

    }

    private void stopRecorder(boolean notifySuccess) {
        try {
            mRecorder.stop();
            mRecorder.reset();
            setState(State.Idle);

            if (notifySuccess) notifyRecordSuccess();

            resetData();
        } catch (Exception e) {
            notifyException(e);
        }
    }

    private void releaseRecorder() {
        mRecorder.release();
        setState(State.Released);
    }

    private void resetData() {
        mRecordFile = null;
    }

    private void notifyRecordSuccess() {
        if (mRecorderParams.getOnRecorderCallback() == null) return;

        long duration = 0;
        if (mStartTime > 0) {
            duration = System.currentTimeMillis() - mStartTime;
        }

        mRecorderParams.getOnRecorderCallback().onRecordSuccess(mRecordFile, duration);
    }

    private void notifyException(Exception e) {
        mRecorder.reset();
        setState(State.Idle);
        resetData();
        HLog.isMRLogE(e.getMessage());
        if (mRecorderParams.getOnExceptionCallback() != null)
            mRecorderParams.getOnExceptionCallback().onException(e);
    }

    public MediaRecorder getRecorder() {
        return mRecorder;
    }

    public enum State {
        /**
         * 空闲
         */
        Idle,
        /**
         * 录音中
         */
        Recording,
        /**
         * 已经释放资源
         */
        Released,

        PAUSE
    }
}
